package com.xilai.cuteBoy.common.util;
import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;

/*************************************************************
 * Description: 计算当前坐标是否在不规则多变形内
 * 算法名称 Ray-casting Algorithm 光线投射算法
 * https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
 * Author: Liu Junbo
 * CreateTime: 2017/7/21
 * Copyright © 西安喜来快递有限公司 All right reserved
 ************************************************************/
public class RayCastingAlgorithm {


    /**
     * 判断当前坐标点,是否在不规则多边形区域内
     * @param point 当前坐标点(经度,纬度)
     * @param points 不规则多边形坐标点集合
     * 从一个点引出一条射线,与多边形的任意若干条边相交,计数器初始化为0,
     * 若相交处被多边形的边从左到右切过,则计数器+1
     * 若相交处被多边形的边从右到左切过,则计数器-1
     * 最后检查计数器 如果是0,点在多边形外,如果!=0 点在多边形内
     * @return
     */
    public static Boolean isWithIn(Point point, List<Point> points){
        //param nonezeroMode True
        Boolean nonezeroMode = Boolean.TRUE;
        int pointSize = points.size();
        //2点成线,>3点成面
        if(pointSize < 3){
            return Boolean.FALSE;
        }
        int j = pointSize - 1;
        Boolean oddNodes = Boolean.FALSE;
        int zeroState = 0;

        for (int k = 0; k < pointSize; k++){

            Point point_k = points.get(k);
            Point point_j = points.get(j);

            BigDecimal x = point_j.getLongitude().subtract(point_k.getLongitude());

            BigDecimal y = point.getLatitude().subtract(point_k.getLatitude());

            BigDecimal y1 = point_j.getLatitude().subtract(point_k.getLatitude());

            BigDecimal x1;

            if(y1.doubleValue() != 0.0){
                x1 = x.multiply(y).divide(y1,6,BigDecimal.ROUND_HALF_UP).add(point_k.getLongitude());
            }else {
                x1 = point_k.getLongitude();
            }

            if((point_k.getLatitude().doubleValue() > point.getLatitude().doubleValue()) != (point_j.getLatitude().doubleValue() > point.getLatitude().doubleValue()) &&
                    point.getLongitude().doubleValue() < x1.doubleValue()){

                oddNodes = Boolean.TRUE;
                if(point_k.getLatitude().doubleValue() > point_j.getLatitude().doubleValue()){
                    zeroState ++;
                }else {
                    zeroState --;
                }
            }
            j = k;
        }
        Boolean result = nonezeroMode?zeroState!=0:oddNodes;
        return result;
    }

    public static void main(String[] args) {

        List<Point> points = new LinkedList<Point>();
        points.add(new Point(new BigDecimal(109.329087),new BigDecimal(34.120046)));
        points.add(new Point(new BigDecimal(109.352261),new BigDecimal(34.128146)));
        points.add(new Point(new BigDecimal(109.349515),new BigDecimal(34.148464)));
        points.add(new Point(new BigDecimal(109.345566),new BigDecimal(34.167925)));
        points.add(new Point(new BigDecimal(109.312607),new BigDecimal(34.167925)));
        points.add(new Point(new BigDecimal(109.285828),new BigDecimal(34.173038)));
        points.add(new Point(new BigDecimal(109.298188),new BigDecimal(34.162811)));
        points.add(new Point(new BigDecimal(109.305741),new BigDecimal(34.148322)));
        points.add(new Point(new BigDecimal(109.329087),new BigDecimal(34.120046)));
        points.add(new Point(new BigDecimal(109.352261),new BigDecimal(34.128146)));
        points.add(new Point(new BigDecimal(109.349515),new BigDecimal(34.148464)));
        points.add(new Point(new BigDecimal(109.345566),new BigDecimal(34.167925)));
        points.add(new Point(new BigDecimal(109.312607),new BigDecimal(34.184399)));
        points.add(new Point(new BigDecimal(109.285828),new BigDecimal(34.173038)));
        points.add(new Point(new BigDecimal(109.298188),new BigDecimal(34.162811)));
        points.add(new Point(new BigDecimal(109.305741),new BigDecimal(34.148322)));


        Point point = new Point(new BigDecimal(108.661651),new BigDecimal(33.912024));
        Boolean result = isWithIn(point,points);
        System.out.println(result);
    }


    /**
     *  定义Point(x,y)
     *  x = longitude
     *  y = latitude
     */
    public static class Point{

        /**
         * 经度
         */
        private BigDecimal longitude;

        /**
         * 维度
         */
        private BigDecimal latitude;


        public Point(BigDecimal longitude, BigDecimal latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        public BigDecimal getLongitude() {
            return longitude;
        }

        public void setLongitude(BigDecimal longitude) {
            this.longitude = longitude;
        }

        public BigDecimal getLatitude() {
            return latitude;
        }

        public void setLatitude(BigDecimal latitude) {
            this.latitude = latitude;
        }
    }
}


